from appengine_django import models as google_models
from google.appengine.ext import db

from gaev import utils
from gaev import exceptions
from gaev import actions
from gaev.humanization import HumanizeContext
from gaev.utils.decorators import memoized

class BaseModel(google_models.BaseModel):
	_model_caption=None
	_human_key=None
	_referring_properties={}
	_inner_property=None
	_ordered_properties=[]
	
	@classmethod
	def get_model_caption(cls):
		if cls._model_caption:
			return cls._model_caption
		from gaev.utils.pluralize import plural
		class_name=cls.__name__
		singluar_name=utils.camelcase_to_words(class_name)
		return plural(singluar_name)
	
	@classmethod
	def get_columns(self):
		return [prop.name for prop in self.get_ordered_properties()]
	
	@classmethod
	def get_column_captions(self, columns):
		props=self.properties()
		return dict( [ (col, props[col].get_caption()) for col in columns] )
	
	cache_capt={}
	def get_caption(self):
		model=self.__class__
		key=self.key()
		if model.get_human_key():
			res=getattr(self, model.get_human_key())
		else:
			res='#capt'
		BaseModel.cache_capt[key]=res
		return res

	@classmethod
	def get_entity_caption(cls,  key):
		cache=BaseModel.cache_capt
		if key in cache:
			return cache[key]
		entity=cls.get(key)
		return entity.get_caption()

	@classmethod
	def get_ordered_properties(cls):
		expl_ord=cls._ordered_properties
		res=expl_ord
		props=cls.properties()
		for prop_id in props:
			prop=props[prop_id]
			if prop not in expl_ord:
				res.append(prop)
		return res
	
	def get_humanized_properties(self, context=HumanizeContext()):
		altered=False
		if not context.entity:
			context.entity=self
			altered=True
		from django.utils.datastructures import SortedDict
		model=self.__class__
		props=model.properties()
		res={}
		for prop_id in props:
			prop=props[prop_id]
			val=getattr(self, prop_id)
			hval=prop.humanize(val, context)
			res[prop_id]=hval
			
		if altered:
			context.entity=None
			
		return res
	def get_properties(self, properties):
		return dict( [ (prop, getattr(self, prop)) for prop in properties] )
	
	
	@classmethod
	def get_human_key(cls):
		return cls._human_key
	@classmethod
	def get_default_ordering(cls):
		if cls.get_human_key():
			return cls.get_human_key()
		tries=[('added','-')]
		for tr in tries:
			if tr[0] in cls.properties():
				return tr[1]+tr[0]
		return None
	
	@classmethod
	def get_entity_set_actions(cls):
		return ['index', 'add', 'delete_all']
	@classmethod
	def get_entity_set_action(cls, name, entity_set):
		from gaev.actions import model as model_actions
		if name == 'index':
			return model_actions.IndexAction(entity_set)
		elif name == 'add':
			return model_actions.AddAction(entity_set)
		elif name == 'delete_all':
			return model_actions.DeleteAllAction(entity_set)
		return None
	def get_actions(self):
		res=['details', 'edit', 'delete']
		model=self.__class__
		if model._inner_property:
			res.append('getin')
		return res
	def get_action(self, name):
		from gaev.actions import model as model_actions
		if name == 'details':
			import gaev.actions.model.details
			return gaev.actions.model.details.Action(self)
		elif name == 'edit':
			return model_actions.EditAction(self)
		elif name == 'delete':
			return model_actions.DeleteAction(self)
		elif name == 'getin':
			return model_actions.GetInAction(self)
		return None
	
	@classmethod
	def register_referring_property(cls, prop):
		rps=BaseModel._referring_properties
		if cls not in rps:
			rps[cls]=[]
		rps[cls].append(prop)
	@classmethod
	def get_referring_properties(cls):
		rps=BaseModel._referring_properties
		if cls in rps:
			return BaseModel._referring_properties[cls]
		else:
			return {}
	
	def get_details_sections(self):
		from gaev.actions.model.details import PropertyListSection
		from gaev.actions.model.details import ActionsSection
		from gaev.actions.model.details import ReferringEntitiesSection
		res=[PropertyListSection(), ActionsSection()]
		for ref_prop in self.get_referring_properties():
			res.append(ReferringEntitiesSection(ref_prop))
		return res
	
	@classmethod
	def entities(cls):
		return EntitySet(cls)
	
class EntitySet(object):
	def __init__(self, model):
		self.model=model
	def all(self):
		return self.model.all()
	def get_by_human_key(self, human_key):
		if not self.model.get_human_key():
			raise exceptions.ModelError
		cond="%s = " % self.model.get_human_key()
		return self.model.all().filter(cond, human_key).get()
	def get(self, key):
		return self.model.get(key)
	def get_action(self, name):
		return self.model.get_entity_set_action(name, self)
	def get_actions(self):
		return self.model.get_entity_set_actions()
	

actions.register_action_getter(BaseModel, lambda obj, name: obj.get_action(name), 
							lambda obj: obj.get_actions(), class_getter=True)
actions.register_action_getter(EntitySet, lambda obj, name: obj.get_action(name), 
							lambda obj: obj.get_actions(), class_getter=True)


def reference_property_config_alterer():
	func=db.ReferenceProperty.__property_config__
	def wrapper(prop, model_class, property_name):
		res=func(prop, model_class, property_name)
		prop.reference_class.register_referring_property(prop)
		return res
	wrapper.__name__ = func.__name__
	wrapper.__dict__ = func.__dict__
	wrapper.__doc__ = func.__doc__
	db.ReferenceProperty.__property_config__=wrapper
reference_property_config_alterer()
